package online.inote.dic.repository.redis;

import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import com.alibaba.fastjson.JSON;

import online.inote.dic.common.utils.StringUtils;

/**
 * @description: redis服务客户端
 * @author XQF.Sui
 * @created 2019年6月10日 下午10:41:23
 * @version 1.0
 */
@Component
public class RedisRepository {
	
	Logger logger = LoggerFactory.getLogger(RedisRepository.class);

	@Autowired
	private StringRedisTemplate template;
	
	/**
	 * <p>
	 * Desc: redis写入String类型数据
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月10日 下午5:40:47
	 * @param key
	 * @param value
	 * @return
	 */
	public boolean set(String key, String value) {
		
		try {
			logger.info("redis写入内容, key:[ {} ], value:[ {} ].", key, value);
			if (StringUtils.isNotBlank(key) && StringUtils.isNotBlank(value)) {
				template.opsForValue().set(key, value);
			}
			return true;
		} catch (Exception e) {
			logger.error("redis写入失败", e);
		}
		
		return false;
	}
	
	/**
	 * <p>
	 * Desc: redis写入String类型数据(超时单位:秒)
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月10日 下午8:52:19
	 * @param key
	 * @param value
	 * @param expiryTime
	 * @return
	 */
	public boolean set(String key, String value, long expiryTime) {
		return set(key, value, expiryTime, TimeUnit.SECONDS);
	}
	
	/**
	 * <p>
	 * Desc: redis写入String类型数据
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月10日 下午8:58:03
	 * @param key
	 * @param value
	 * @param expiryTime
	 * @param paramTimeUnit
	 * @return
	 */
	public boolean set(String key, String value, long timeout, TimeUnit unit) {
		try {
			logger.info("redis写入内容, key:[ {} ], value:[ {} ], expiryTime:[ {} ], TimeUnit:[ {} ].", key, value, timeout,
					unit.toString());
			if (StringUtils.isNotBlank(key) && StringUtils.isNotBlank(value)) {
				template.opsForValue().set(key, value, timeout, unit);
				return true;
			}
		} catch (Exception e) {
			logger.error("redis写入失败", e);
		}
		
		return false;
	}
	
	/**
	 * <p>
	 * Desc: 获取redis数据
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月10日 下午5:44:09
	 * @param key
	 * @return
	 */
	public String get(String key) {
		logger.info("获取redis数据, key:[ {} ].", key);
		
		if (StringUtils.isBlank(key)) {
			logger.info("获取redis数据失败, KEY不能为空.");
			throw new RuntimeException("获取redis数据失败, KEY不能为空.");
		}
		
		return template.opsForValue().get(key);
	}
	
	/**
	 * <p>
	 * Desc: 是否包含KEY
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月10日 下午5:46:51
	 * @param key
	 * @return
	 */
	public boolean hasKey(String key) {
		logger.info("查询redis是否存在key:[ {} ].", key);
		
		if (StringUtils.isBlank(key)) {
			logger.info("查询redis是否存在key失败, key不能为空.");
			throw new RuntimeException("查询redis是否存在key失败, key不能为空.");
		}
		
		return template.hasKey(key);
	}
	
	/**
	 * <p>
	 * Desc: 增量
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月10日 下午5:52:30
	 * @param key
	 * @param value
	 * @return
	 */
	public Long increment(String key, int value) {
		logger.info("增量redis数据, key:[ {} ], value:[ {} ].", key, value);
		
		if (StringUtils.isBlank(key)) {
			logger.info("增量redis数据失败, KEY不能为空.");
			throw new RuntimeException("增量redis数据失败, KEY不能为空.");
		}
		
		return template.opsForValue().increment(key, value);
	}
	
	/**
	 * <p>
	 * Desc: 写入redis set数据
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月10日 下午6:10:28
	 * @param key
	 * @param set
	 * @return
	 */
	public boolean addSet(String key, Set<String> set) {
		
		try {
			logger.info("写入redis set数据, key:[ {} ].", key);
			
			if (StringUtils.isBlank(key)) {
				logger.info("写入redis set数据失败, KEY不能为空.");
			} else if (CollectionUtils.isEmpty(set)) {
				logger.info("写入redis set数据失败, set不能为空.");
			} else {
				template.opsForSet().add(key, set.toArray(new String[set.size()]));
				return true;
			}
		} catch (Exception e) {
			logger.error("写入redis set数据失败", e);
		}
		
		return false;
	}
	
	public boolean updateSet(String key, Set<String> set) {
		
		try {
			deleteKey(key);
			logger.info("写入redis set数据, key:[ {} ].", key);
			
			if (StringUtils.isBlank(key)) {
				logger.info("写入redis set数据失败, KEY不能为空.");
			} else if (CollectionUtils.isEmpty(set)) {
				logger.info("写入redis set数据失败, set不能为空.");
			} else {
				template.opsForSet().add(key, set.toArray(new String[set.size()]));
				return true;
			}
		} catch (Exception e) {
			logger.error("写入redis set数据失败", e);
		}
		
		return false;
	}
	
	/**
	 * <p>
	 * Desc: 写入redis set数据
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月10日 下午6:08:06
	 * @param key
	 * @param values
	 * @return
	 */
	public boolean setSet(String key, String... values) {
		
		try {
			logger.info("写入redis set数据, key:[ {} ], values:[ {} ].", key, JSON.toJSONString(values));
			
			if (StringUtils.isBlank(key)) {
				logger.info("写入redis set数据失败, KEY不能为空.");
			} else if (values == null || values.length == 0) {
				logger.info("写入redis set数据失败, 数据不能为空.");
			} else {
				template.opsForSet().add(key, values);
				return true;
			}
		} catch (Exception e) {
			logger.error("写入redis set数据失败", e);
		}
		
		return false;
	}
	
	/**
	 * <p>
	 * Desc: 获取redis set数据
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月10日 下午6:20:36
	 * @param key
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> Set<T> getSet(String key) {
		logger.info("获取redis set数据, key:[ {} ]", key);
		
		if (StringUtils.isBlank(key)) {
			logger.info("获取redis set数据失败, KEY不能为空.");
			throw new RuntimeException("获取redis set数据失败, KEY不能为空.");
		}
		
		return (Set<T>) template.opsForSet().members(key);
	}
	
	/**
	 * <p>
	 * Desc: 查看集合中是否存在指定数据
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月10日 下午9:17:30
	 * @param key
	 * @param o
	 * @return
	 */
	public Boolean isMember(String key, Object o) {
		logger.info("查看集合中是否存在指定数据, key:[ {} ], o:[ {} ].", key, o.toString());
		
		if (StringUtils.isBlank(key)) {
			logger.info("查看集合中是否存在指定数据失败, key不能为空.");
			throw new RuntimeException("查看集合中是否存在指定数据失败, key不能为空.");
		}
		
		return template.opsForSet().isMember(key, o);
	}
	
	/**
	 * <p>
	 * Desc: 获取KEY是指定前缀的数据
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月10日 下午6:22:52
	 * @param prefixKey key前缀
	 * @param isCompriseCurrentKey 是否包含当前key
	 * @return
	 */
	public <T> Set<T> keys(String keyPrefix) {
		logger.info("获取redis数据, keyPrefix:[ {} ].", keyPrefix);
		
		if (StringUtils.isBlank(keyPrefix)) {
			logger.info("获取redis数据失败, prefixKey不能为空.");
			throw new RuntimeException("获取redis数据失败, keyPrefix不能为空.");
		}
		
		if (keyPrefix.endsWith("*")) {
			return getMultipleKey(template.keys(keyPrefix));
		}
		
		return getMultipleKey(template.keys(keyPrefix + "*"));
	}
	
	/**
	 * <p>
	 * Desc: 根据集合Key获取redis数据
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月10日 下午6:38:25
	 * @param keySet
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> Set<T> getMultipleKey(Set<String> keySet) {
		logger.info("根据集合Key获取redis数据, keySet:[ {} ].", JSON.toJSONString(keySet));
		
		if (CollectionUtils.isEmpty(keySet)) {
			logger.info("根据集合Key获取redis数据, keySet为空.");
			throw new RuntimeException("获取redis数据失败, keySet为空.");
		}
		
		return (Set<T>) template.opsForSet().union(null, keySet);
	}
	
	/**
	 * <p>
	 * Desc: 设置过期时间(单位:秒)
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月10日 下午9:13:54
	 * @param key
	 * @param timeout
	 * @return
	 */
	public Boolean expire(String key, long timeout) {
		return expire(key, timeout, TimeUnit.SECONDS);
	}
	
	/**
	 * <p>
	 * Desc: 设置过期时间
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月10日 下午9:12:57
	 * @param key
	 * @param timeout
	 * @param unit
	 * @return
	 */
	public Boolean expire(String key, long timeout, TimeUnit unit) {
		logger.info("设置redis数据过期时间, key:[ {} ], timeout:[ {} ], TimeUnit:[ {} ].", key, timeout, unit.toString());
		
		if (StringUtils.isBlank(key)) {
			logger.info("设置redis数据过期时间失败, key不能为空.");
			throw new RuntimeException("设置redis数据过期时间失败, key不能为空.");
		}
		
		return template.expire(key, timeout, unit);
	}
	
	/**
	 * <p>
	 * Desc: 获取过期时间
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月10日 下午9:09:17
	 * @param key
	 * @return
	 */
	public Long getExpiry(String key) {
		logger.info("获取redis数据过期时间, key:[ {} ].", key);
		
		if (StringUtils.isBlank(key)) {
			logger.info("获取redis数据过期时间失败, key不能为空.");
			throw new RuntimeException("获取redis数据过期时间失败, key不能为空.");
		}
		
		return template.getExpire(key);
	}
	
	/**
	 * <p>
	 * Desc: 删除redis数据
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月10日 下午5:48:56
	 * @param key
	 * @return
	 */
	public void deleteKey(String key) {
		logger.info("删除redis数据, key:[ {} ].", key);
		
		if (StringUtils.isBlank(key)) {
			logger.info("删除redis数据失败, KEY不能为空.");
			throw new RuntimeException("删除redis数据失败, KEY不能为空.");
		}
		
		template.delete(key);
	}
	

	
	/**
	 * <p>
	 * Desc: 根据KEY前缀删除redis数据
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月10日 下午6:47:59
	 * @param keyPrefix
	 * @return
	 */
	public void deleteKeyPrefix(String keyPrefix) {
		logger.info("删除redis数据, keyPrefix:[ {} ].", keyPrefix);
		
		if (StringUtils.isBlank(keyPrefix)) {
			logger.info("删除redis数据失败, keyPrefix不能为空.");
			throw new RuntimeException("删除redis数据失败, keyPrefix不能为空.");
		}
		
		Set<String> keySet;
		
		if (keyPrefix.endsWith("*")) {
			keySet = template.keys(keyPrefix);
		} else {
			keySet = template.keys(keyPrefix + "*");
		}
		
		template.delete(keySet);
	}

	/**
	 * <p>
	 * Desc: 更新redis内容(主要用于有超时时间的数据)
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月10日 下午9:51:19
	 * @param key
	 * @param value
	 * @return 
	 */
	public boolean update(String key, String value) {
		logger.info("更新redis数据, key:[ {} ]", key);
		
		if (hasKey(key)) {
			return set(key, value, getExpiry(value), TimeUnit.SECONDS);
		} else {
			return set(key, value);
		}
	}
}